module EnvRust where

import qualified Data.Map as Map
import Data.List
import AbsRust
import PrintRust
import Data.Char

type Env = (Functions, Variables)
type Functions = Map.Map FunIdent [FunInfo]
type Variables = Map.Map Id [VarInfo]

updatePath :: String -> Path -> Path
updatePath = (:)

updateBkt :: Int -> Int
updateBkt = (+1)

initPath = ["main"]

initBlockTemp :: Int
initBlockTemp = 0

initDefs = []

--inserisce variabile singola o tupla nella lista delle variabili definite in un let
insertLet ::(Maybe (RExpr,Type)) -> Path -> Var -> DefVars -> DefVars
insertLet Nothing _ _ dv = dv
insertLet _ pat (Var v _) dv = insertId pat v dv
insertLet m pat (VTup vs) dv = foldr (insertLet m pat) dv vs 

insertId :: Path -> Id -> DefVars -> DefVars
insertId pat i dv= (i,pat):dv

--in un assegnamento, inserisce id nella lista delle variabili definite, purchè la variabile sia definita nell'environment.
--In caso di variabile giù presente, la reinserisce con il path attuale
insertAssgnVar :: Env -> Path -> Id -> DefVars -> DefVars
insertAssgnVar e pat i dv= case lookupVar e i pat of
   []->dv
   (x:_)-> insertId pa i dv
      where
         pa = vPath x
unmodDefVars _ = id

--inserisce le variabili formali di una chiamata di funzione nel defVars
--in questo modo eventuali riferimenti non porteranno errori dovuti al fatto che il 
--parametro possa risultare non inizializzato. Nel caso di tuple, l'inserimento avviene esplorando 
--fino alle var semplici
formParam :: Path -> DefVars -> [Parameter] -> DefVars
formParam pat dv ps = foldr (fParam) dv rs
   where
      rs = map pvar (reverse ps)
      fParam (Var i _) defs = insertId pat i defs
      fParam (VTup vs) defs = foldr fParam defs (reverse vs)     
      
      
funInfo :: [Parameter] -> Type -> Path -> FunInfo
funInfo ps t pt = FI ps t 0 pt

updateFunInfo (FI ps t _ pt) n = (FI ps t n pt)

funIdent :: Id -> [a] -> FunIdent
funIdent i ps = Fun i $ length ps

vStaticInfo :: Type -> (RExpr,Type) -> Path -> VarInfo
vStaticInfo = VStatic

varInfo :: Attribute -> Type -> Path -> VarInfo
varInfo AMut t pat = VVar t pat
varInfo AConst t pat = VConst t pat

vType :: Error -> VarInfo -> Type
vType _ (VVar t _) = t
vType _ (VConst t _) =  t
vType _ (VStatic t _ _) = t
vType _ (VPar _ t _) = t
vType err (VErr) = TErr err

vPath :: VarInfo -> Path
vPath (VVar _ p) = p
vPath (VConst _ p) = p
vPath (VStatic _ _ p) = p
vPath (VPar _ _ p) = p

fType :: FunInfo -> Maybe Type
fType (FI _ t _ _) = Just t
fType (FErr) = Nothing

fMaybeNum [] = Nothing
fMaybeNum (f:_) = case f of
   FI _ _ n _ -> Just n
   _ -> Nothing

emptyEnv:: Env
emptyEnv = (Map.empty, Map.empty)


--crea un nuovo environment e inserisce le procedure predefinite crete con defaultFun
initEnv:: Env
initEnv = foldr f emptyEnv defaultFun
   where 
      f (i,fi) e= addFun e i fi 
      
defaultFun = concat r
   where
      r = map f [("read",Reference),("write",Constant)]
      f (fun,mod) = dFun fun mod
      
dFun fun mod = map (agr mod) basicTypes  
   where
      basicTypes = [BasicType BTInt, BasicType BTFloat, BasicType BTChar, BasicType BTString]
      agr m t = (i,fs)
         where
            ps = defaultPar m t
            fs = funInfo ps tu initPath
            i = funIdent fun ps
            tu = BasicType BTUnit
            defaultPar md typ = [Param md (Var "x" "") typ]  


--la funzione effettua il lookup sull'environment passato come argomento e vi ricerca l'id specificato.
--tutte le corrispondenze vengono estratte. Nel caso in cui non vi sia corrispondenza con l'id ricercato, restituirà una lista vuota
--altrimenti la lista delle funzioni visibili a partire dal path considerato. La selezione tiene conto del tipo di variabile nel determinarne la 
--visibilità   
lookupVar:: Env -> Id -> Path -> [VarInfo]
lookupVar e v p = case Map.lookup v $ snd e of
 		Nothing -> []
		Just vi -> filter (f p) vi
   where
      f p (VStatic _ _ pt) = length p >= length pt && all (==True) (zipWith (==) (reverse p) (reverse pt))
      f p (VVar _ pt) = validSubPath p pt
      f p (VConst _ pt) = validSubPath p pt
      f p (VPar _ _ pt) = validSubPath p pt

validSubPath ::Path -> Path -> Bool      
validSubPath p pt= lpt<=lp && aF p == aF pt || pt == initPath
   where
      lp=length p
      lpt= length pt
      zs = zipWith (==) (reverse pt) (reverse p)
      aD= all isDigit
      aT= all (== True) zs
      aF = dropWhile aD
      
      
--ricerca nell'environment tutte le funzioni corrispondenti con il FunIdent specificato e restituisce la lista (eventualmente vuota)
--di quelle visibili
lookupFun:: Env -> FunIdent -> Path -> [FunInfo]
lookupFun e fi p = case Map.lookup fi $ fst e of
		Nothing -> []
		Just fs -> filter (f p) fs
   where
      f p (FI _ _ _ pt) = length p >= length pt && all (==True) (zipWith (==) (reverse p) (reverse pt))


--preso un environment, un id e un varInfo, provvede al lookup nell'environment specificato: se il risultato è Nothing, inserisce la variabile 
--nell'environment e lo restituisce. Altrimenti, inserisce la variabile in ordine rispetto alla lunghezza del path e aggiorna l'environment
addVar:: Env -> Id -> VarInfo -> Env
addVar e v vi = case Map.lookup v $ snd e of
   Nothing -> (fst e, Map.insert v [vi] (snd e))
   Just vs -> updateVarEnv e v vi vs 

updateVarEnv :: Env -> Id -> VarInfo -> [VarInfo] -> Env
updateVarEnv e v vi vs = (fst e, Map.insert v rs (snd e))
   where
      rs = insertOverVar vi vs

insertOverVar vi vs = insertBy compVarInfo vi vs

compVarInfo v1 v2
   |l1 >= l2 = LT
   |otherwise = GT
   where
      l1 = length $ vPath v1
      l2 = length $ vPath v2


--preso un environment, un funIdent e un funInfo, provvede a fornire un environment aggiornato con la funzione specificata. Nel caso di 
--funzione già esistente, la inserisce con giusto ordinamento in termini di path e tipo parametri.
addFun:: Env -> FunIdent -> FunInfo -> Env
addFun e i fi = case Map.lookup i $ fst e of
		Nothing -> (Map.insert i [fi] (fst e), snd e)
		Just fs -> updateFunEnv e i fi fs
		
updateFunEnv:: Env -> FunIdent -> FunInfo -> [FunInfo] -> Env		
updateFunEnv e i fi fs = (Map.insert i rs (fst e), snd e)
   where
      rs = insertOverFun f fs
      f = updateFunInfo fi n
      n = length fs
      
insertOverFun fi fs = insertBy compFunInfo fi fs

compFunInfo (FI ps1 _ _ pt1) (FI ps2 _ _ pt2)
   |l1 < l2 = LT
   |l1 > l2 = GT
   | otherwise = foldr f EQ $ reverse rs
   where
      l1 = length pt1
      l2 = length pt2
      rs = zipWith compParameter ps1 ps2
      f LT GT = GT
      f LT _ = LT
      f EQ a = a
      f GT LT = LT
      f GT _ = GT

compParameter (Param _ _ t1) (Param _ _ t2) = compare t1 t2

addFunPar :: Path -> Id -> [Parameter] -> Type -> Env -> Env
addFunPar pat i ps t env = addFun e fi fun
   where
      fi = funIdent i ps
      fun = funInfo ps t funpat
      funpat = tail pat
      e = addPars pat ps env


--Preso un environment, path e lista parametri di una funzione, li inserisce nell'environment
addPars :: Path -> [Parameter] -> Env -> Env      
addPars pat ps env = foldr (addVPar pat) env (reverse ps)

addVPar :: Path -> Parameter -> Env -> Env
addVPar pat (Param m v t) env = aVar vP pat v t env
   where
      vP = VPar m

aVar varI pat (Var v _) t env = addVar env v vi
   where
      vi = varI t pat
aVar varI pat (VTup vs) (Tuple ts) env
   |vn <= tn = agr ts
   |otherwise = agr tu
   where
      agr t = foldr p env (zs t)
      zs tt = zipWith f (reverse vs) (reverse tt)
      f a b = (a,b)
      p (a,b) e = aVar varI pat a b e
      tu = ts ++ replicate (vn - tn) (BasicType BTUnit)
      vn = length vs
      tn = length ts
aVar varI pat v@(VTup vs) t env = aVar varI pat v ts env
   where
      ts = Tuple $ replicate vn t
      vn = length vs

--dato un environment, un path, un attributo, una Var e un tipo, provvede al corretto inserimento della var nell'enviroment
addLetVar:: Env-> Path -> Attribute -> Var -> Type -> Env
addLetVar env pat att var t = aVar v pat var t env
   where
      v = f att
      f AConst = VConst
      f AMut = VVar

